{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "eIrvnAbGZ1wP"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "form",
        "colab": {},
        "colab_type": "code",
        "id": "_A4IPZ-WZ9H7"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Y6Upshw5Stlj"
      },
      "source": [
        "# \"Επικάλυψη\"(masking) και \"γέμισμα\"(padding) με την Keras\n",
        "\n",
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/guide/keras/masking_and_padding\">\n",
        "    <img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />\n",
        "    Άνοιγμα στο TensorFlow.org</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/el/guide/keras/masking_and_padding.ipynb\">\n",
        "    <img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />\n",
        "    Εκτέλεση στο Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/el/guide/keras/masking_and_padding.ipynb\">\n",
        "    <img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />\n",
        "    Προβολή πηγαίου στο GitHub</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/el/guide/keras/masking_and_padding.ipynb\">\n",
        "    <img src=\"https://www.tensorflow.org/images/download_logo_32px.png\" />\n",
        "    Λήψη \"σημειωματάριου\"</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Q8oYZYDyyEWe"
      },
      "source": [
        "Note: Η κοινότητα του TensorFlow έχει μεταφράσει αυτά τα έγγραφα. Καθότι οι μεταφράσεις αυτές αποτελούν την καλύτερη δυνατή προσπάθεια , δεν υπάρχει εγγύηση ότι θα παραμείνουν ενημερωμένες σε σχέση με τα [επίσημα Αγγλικά έγγραφα](https://www.tensorflow.org/?hl=en).\n",
        "Αν έχετε υποδείξεις για βελτίωση των αρχείων αυτών , δημιουργήστε ένα pull request στο [tensorflow/docs](https://github.com/tensorflow/docs) GitHub repository . Για να συμμετέχετε στη σύνταξη ή στην αναθεώρηση των μεταφράσεων της κοινότητας , επικοινωνήστε με το [docs@tensorflow.org list](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "MZDRE9jWUVeU"
      },
      "source": [
        "# Διάταξη\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "wJEBe8hTlB6W"
      },
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "\n",
        "import tensorflow as tf\n",
        "\n",
        "from tensorflow.keras import layers"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "R9-kpAcgUeg5"
      },
      "source": [
        "# \"Γέμισμα\" σειριακών δεδομένων\n",
        "\n",
        "Κατά την επεξεργασία ακολουθιών δεδομένων, είναι σύνηθες ορισμένα δείγματα να έχουν διαφορετικό μήκος. Για παράδειγμα (το κείμενο ομαδοποιείται κατά λέξεις) :\n",
        "\n",
        "```\n",
        "[\n",
        "  [\"The\", \"weather\", \"will\", \"be\", \"nice\", \"tomorrow\"],\n",
        "  [\"How\", \"are\", \"you\", \"doing\", \"today\"],\n",
        "  [\"Hello\", \"world\", \"!\"]\n",
        "]\n",
        "```\n",
        "Μετά από λεξιλογική αναζήτηση , τα δεδομένα μπορούν να εμφανιστούν ως ακέραιοι :\n",
        "\n",
        "```\n",
        "[\n",
        "  [83, 91, 1, 645, 1253, 927],\n",
        "  [73, 8, 3215, 55, 927],\n",
        "  [71, 1331, 4231]\n",
        "]\n",
        "```\n",
        "\n",
        "Το παραπάνω πρόκειται για μία δισδιάστατη λίστα όπου ορισμένα δείγματα έχουν μήκος 6,5, και 3 αντίστοιχα. Επειδή τα δεδομένα εισόδου για ένα μοντέλο μάθησης εις βάθος πρέπει να είναι ένας τανυστής(π.χ `batch_size, 6 , vocab_size` στην παραπάνω περίπτωση), δείγματα τα οποία είναι μικρότερα από το μεγαλύτερο στοιχείο , πρέπει να \"γεμίσουν\" με κάποια συμβολική τιμή υποκατάστασης (εναλλακτικά, κάποιος μπορεί να \"κόψει\" τα μεγάλα δείγματα αντί να \"γεμίσει\" τα μικρότερα σε μήκος δείγματα).\n",
        "\n",
        "Η Keras παρέχει ένα API μέσω του οποίου μπορείτε εύκολα να \"κόψετε\" και να \"γεμίσετε\" τις ακολουθίες δεδομένων : `tf.keras.preprocessing.sequence.pad_sequences`"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "xI-lHnyxfa2T"
      },
      "outputs": [],
      "source": [
        "raw_inputs = [\n",
        "  [83, 91, 1, 645, 1253, 927],\n",
        "  [73, 8, 3215, 55, 927],\n",
        "  [711, 632, 71]\n",
        "]\n",
        "\n",
        "# By default, this will pad using 0s; it is configurable via the\n",
        "# \"value\" parameter.\n",
        "# Note that you could \"pre\" padding (at the beginning) or\n",
        "# \"post\" padding (at the end).\n",
        "# We recommend using \"post\" padding when working with RNN layers\n",
        "# (in order to be able to use the \n",
        "# CuDNN implementation of the layers).\n",
        "padded_inputs = tf.keras.preprocessing.sequence.pad_sequences(raw_inputs,\n",
        "                                                              padding='post')\n",
        "\n",
        "print(padded_inputs)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "6S-RzxudYfHN"
      },
      "source": [
        "# \"Επικάλυψη\"\n",
        "\n",
        "Τώρα που όλα τα δείγματα έχουν όμοιο μήκος , το μοντέλο πρέπει κάπως να ενημερωθεί ότι μέρος των δεδομένων είναι στην πραγματικότητα \"γέμισμα\" και πρέπει να αγνοηθεί. Ο μηχανισμός αυτός λέγεται \"επικάλυψη\".\n",
        "\n",
        "Υπάρχουν τρείς τρόποι εισαγωγής \"επικάλυψης\" στα μοντέλα Keras :\n",
        "\n",
        "* Προσθήκη ενός επιπέδου `keras.layers.Masking`.\n",
        "* Ρύθμιση ενός επιπέδου `keras.layers.Embedding` με `mask_zero=True.\n",
        "* Πέρασμα ενός ορίσματος `mask` χειροκίνητα , όταν καλούνται επίπεδα που το υποστηρίζουν (π.χ RNN επίπεδα)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "xMOZAMIIZ1xm"
      },
      "source": [
        "# Επίπεδα \"επικάλυψης\" : ενσωμάτωση και \"επικάλυψη\"\n",
        "\n",
        "Στο παρασκήνιο, αυτά τα επίπεδα θα δημιουργήσουν έναν τανιστή επικάλυψης(2D τανιστής με μορφή `(batch, sequence_length())`,και θα τον ενσωματώσουν στον τανιστή εξόδου,τον οποίο επιστρέφει το επίπεδο `Επικάλυψης` ή `Ενσωμάτωσης`.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "rYXQ589PkC0P"
      },
      "outputs": [],
      "source": [
        "embedding = layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)\n",
        "masked_output = embedding(padded_inputs)\n",
        "\n",
        "print(masked_output._keras_mask)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "-0VVscXQm1D1"
      },
      "outputs": [],
      "source": [
        "masking_layer = layers.Masking()\n",
        "# Simulate the embedding lookup by expanding the 2D input to 3D,\n",
        "# with embedding dimension of 10.\n",
        "unmasked_embedding = tf.cast(\n",
        "    tf.tile(tf.expand_dims(padded_inputs, axis=-1), [1, 1, 10]),\n",
        "    tf.float32)\n",
        "\n",
        "masked_embedding = masking_layer(unmasked_embedding)\n",
        "print(masked_embedding._keras_mask)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "rfL05aqJa8_A"
      },
      "source": [
        "Η μάσκα είναι ένας δισδιάστατος boolean τανιστής με μορφή `(batch_size , sequence_length)`,όπου κάθε `Ψευδής` καταχώριση υποδεικνύει ότι το αντίστοιχο χρονικό σημείο θα πρέπει να αγνοηθεί κατά την επεξεργασία. "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "r6pHlQWGbzUi"
      },
      "source": [
        "# Αναπαραγωγή επικάλυψης στα Functional API και Sequential API\n",
        "\n",
        "Όταν χρησιμοποιείται το Functional ή το Sequential API , μία επικάλυψη που έχει δημιουργειθεί από ένα επίπεδο `Ενσωμάτωσης` ή `Επικάλυψης` θα αναπαραχθεί εντός του δικτύου για κάθε επίπεδο το οποίο είναι σε θέση να τη χρησιμοποιήσει(π.χ RNN επίπεδα). Η επικάλυψη που αντιστοιχεί σε μία είσοδο περνά σε κάθε επίπεδο που μπορεί να τη χρησιμοποιήσει - αυτό γίνεται αυτόματα από την Keras -.\n",
        "\n",
        "Σημειώνεται ότι στη μέθοδο `call` μίας υποκλάσσης μοντέλου ή επιπέδου , οι μάσκες δεν αναπαράγωνται αυτόματα , οπότε θα χρειαστεί να περάσει χειροκίνητα το όρισμα `mask` σε όποιο επίπεδο το χρειάζεται. Δείτε παρακάτω για λεπτομέρειες.\n",
        "\n",
        "Π.χ , στο ακόλουθο Sequential μοντέλο , το επίπεδο `LSTM` θα δεχτεί αυτόματα τη μάσκα , οπότε θα αγνοήσει τις \"γεμισμένες\" τιμές :"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "zfkxyf7yVyxJ"
      },
      "outputs": [],
      "source": [
        "model = tf.keras.Sequential([\n",
        "  layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True),\n",
        "  layers.LSTM(32),\n",
        "])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "dwxrfavkd4uU"
      },
      "source": [
        "Όμοια ,  για το Functional API μοντέλο : "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "SYaVl6WSWJal"
      },
      "outputs": [],
      "source": [
        "inputs = tf.keras.Input(shape=(None,), dtype='int32')\n",
        "x = layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)(inputs)\n",
        "outputs = layers.LSTM(32)(x)\n",
        "\n",
        "model = tf.keras.Model(inputs, outputs)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "hkieyrQieUBu"
      },
      "source": [
        "# Πέρασμα τανιστών \"επικάλυψης\" απευθείας σε επίπεδο\n",
        "\n",
        "Επίπεδα τα οποία μπορούν να διαχειριστούν μάσκες(όπως το `LSTM`) έχουν όρισμα `mask` στην μέθοδο `__call__` τους.\n",
        "\n",
        "Στο μεταξύ , επίπεδα τα οποία παράγουν μάσκες (π.χ `Ενσωμάτωσης`) εκθέτουν την μέθοδο `compute_mask(input, previous_mask)` την οποία μπορείτε να καλέσετε.\n",
        "\n",
        "Επομένως, μπορείτε να κάνετε κάτι τέτοιο :\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "coCV26fqXmya"
      },
      "outputs": [],
      "source": [
        "class MyLayer(layers.Layer):\n",
        "  \n",
        "  def __init__(self, **kwargs):\n",
        "    super(MyLayer, self).__init__(**kwargs)\n",
        "    self.embedding = layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)\n",
        "    self.lstm = layers.LSTM(32)\n",
        "    \n",
        "  def call(self, inputs):\n",
        "    x = self.embedding(inputs)\n",
        "    # Note that you could also prepare a `mask` tensor manually.\n",
        "    # It only needs to be a boolean tensor\n",
        "    # with the right shape, i.e. (batch_size, timesteps).\n",
        "    mask = self.embedding.compute_mask(inputs)\n",
        "    output = self.lstm(x, mask=mask)  # The layer will ignore the masked values\n",
        "    return output\n",
        "\n",
        "layer = MyLayer()\n",
        "x = np.random.random((32, 10)) * 100\n",
        "x = x.astype('int32')\n",
        "layer(x)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "UI6S3oowfFJi"
      },
      "source": [
        "# Υποστήριξη masking σε δικά σας layers \n",
        "\n",
        "Μερικές φορές , μπορεί να χρειαστεί να γράψετε επίπεδα τα οποία παράγουν μία μάσκα (π.χ `Ενσωμάτωσης`) ή επίπεδα τα οποία θα χρειάζεται να τροποποιούν την τρέχουσα μάσκα.\n",
        "\n",
        "Για παράδειγμα, κάθε επίπεδο το οποίο παράγει έναν τανιστή με διαφορετική διάσταση χρόνου από ότι στην είσοδο του , όπως το επίπεδο `Συγχώνευση` (Concatenate) το οποίο συγχωνεύει στη διάσταση του χρόνου, θα χρειάζεται να τροποποιεί την τρέχουσα μάσκα έτσι ώστε τα χαμηλότερα επίπεδα να μπορούν να λάβουν υπ'όψιν τις κατάλληλες χρονοσημάνσεις.\n",
        "\n",
        "Για να γίνει το παραπάνω,το επίπεδο σας πρέπει να υλοποιεί την μέθοδο `layer.compute_mask()`, η οποία παράγει μία νέα μάσκα , με βάση  τη δοσμένη είσοδο και την τρέχουσα μάσκα\n",
        "\n",
        "Τα περισσοτερα επίπεδα δεν τροποποιούν τη διάσταση του χρόνου , επομένως δεν χρειάζεται να ανησυχείτε για την επικάλυψη. Η προεπιλεγμένη συμπεριφορά της `compute_mask()` είναι απλά να περνάει την τρέχουσα μάσκα στο δίκτυο , όταν αυτό χρειάζεται.\n",
        "Ακολουθεί παράδειγμα με το επίπεδο `TemporalSplit` το οποίο χρειάζεται να τροποποιήσει την τρέχουσα μάσκα: "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "gaS_7dXyr-Z0"
      },
      "outputs": [],
      "source": [
        "class TemporalSplit(tf.keras.layers.Layer):\n",
        "  \"\"\"Split the input tensor into 2 tensors along the time dimension.\"\"\"\n",
        "\n",
        "  def call(self, inputs):\n",
        "    # Expect the input to be 3D and mask to be 2D, split the input tensor into 2\n",
        "    # subtensors along the time axis (axis 1).\n",
        "    return tf.split(inputs, 2, axis=1)\n",
        "    \n",
        "  def compute_mask(self, inputs, mask=None):\n",
        "    # Also split the mask into 2 if it presents.\n",
        "    if mask is None:\n",
        "      return None\n",
        "    return tf.split(mask, 2, axis=1)\n",
        "\n",
        "first_half, second_half = TemporalSplit()(masked_embedding)\n",
        "print(first_half._keras_mask)\n",
        "print(second_half._keras_mask)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "TesKHfZUhWRy"
      },
      "source": [
        "Άλλο ένα παράδειγμα , ενός επιπέδου `CustomEmbedding` το οποίο είναι ικανό να παράγει μάσκα από τις δοσμένες τιμές εισόδου :"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "fLSpf1iojSO7"
      },
      "outputs": [],
      "source": [
        "class CustomEmbedding(tf.keras.layers.Layer):\n",
        "  \n",
        "  def __init__(self, input_dim, output_dim, mask_zero=False, **kwargs):\n",
        "    super(CustomEmbedding, self).__init__(**kwargs)\n",
        "    self.input_dim = input_dim\n",
        "    self.output_dim = output_dim\n",
        "    self.mask_zero = mask_zero\n",
        "    \n",
        "  def build(self, input_shape):\n",
        "    self.embeddings = self.add_weight(\n",
        "      shape=(self.input_dim, self.output_dim),\n",
        "      initializer='random_normal',\n",
        "      dtype='float32')\n",
        "    \n",
        "  def call(self, inputs):\n",
        "    return tf.nn.embedding_lookup(self.embeddings, inputs)\n",
        "  \n",
        "  def compute_mask(self, inputs, mask=None):\n",
        "    if not self.mask_zero:\n",
        "      return None\n",
        "    return tf.not_equal(inputs, 0)\n",
        "  \n",
        "  \n",
        "layer = CustomEmbedding(10, 32, mask_zero=True)\n",
        "x = np.random.random((3, 10)) * 9\n",
        "x = x.astype('int32')\n",
        "\n",
        "y = layer(x)\n",
        "mask = layer.compute_mask(x)\n",
        "\n",
        "print(mask)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "8L_pfLQYh2jw"
      },
      "source": [
        "# Συγγραφή επιπέδων που χρειάζονται πληροφορίες της μάσκας\n",
        "\n",
        "Μερικά επίπεδα είναι *καταναλωτές*  μασκών · δέχονται ένα όρισμα `mask` στην `call` και το χρησιμοποιούν ώστε να προσδιορίσουν αν θα αγνοήσουν συγκεκριμένες χρονοσημάνσεις.\n",
        "Για να γράψετε είναι τέτοιο επίπεδο , μπορείτε απλά να προσθέσετε το όρισμα `mask=None` στην υπογραφή της μεθόδου `call`. Η μάσκα που συσχετίζεται με τις εισόδους θα περάσει στο επίπεδο σας όποτε είναι διαθέσιμη.\n",
        "\n",
        "```python\n",
        "class MaskConsumer(tf.keras.layers.Layer):\n",
        "  \n",
        "  def call(self, inputs, mask=None):\n",
        "    ...\n",
        "```\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "2ksnf30YiuSf"
      },
      "source": [
        "# Σύνοψη\n",
        "\n",
        "Αυτά αποτελούν όσα χρειάζεστε για το masking στην Keras . Για να συνοψίσουμε :\n",
        "\n",
        "* \"Masking\" είναι ο τρόπος ,με τον οποίο τα επίπεδα είναι ικανά να γνωρίζουν πότε να αγνοήσουν συγκεκριμένες χρονοσημάνσεις σε ακολουθίες δεδομένων.\n",
        "* Μερικά επίπεδα είναι γεννήτορες μασκών : το `Ενσωμάτωση` μπορεί να παράγει μάσκα από τιμές εισόδου (αν `mask_zero=True`), ομοιώς και το επίπεδο `Masking`.\n",
        "* Μερικά επίπεδα είναι καταναλωτές μασκών : \"εκθέτουν\" ένα όρισμα `mask` στην μέθοδο `__call__` τους . Αυτή είναι η περίπτωση των RNN δικτύων.\n",
        "* Στα Functional και Sequential API's , οι πληροφορίες επικάλυψης διαχέονται αυτόματα.\n",
        "* Κατά την σύνταξη υποκλάσσεων μοντέλων ή όταν χρησιμοποιούνται επίπεδα κατά αυτοτελή τρόπο , το όρισμα `mask` πρέπει να περνάει στα επίπεδα χειροκίνητα.\n",
        "* Μπορείτε εύκολα να συντάξετε επίπεδα τα οποία να τροποποιούν την τρέχουσα μάσκα, να παράγουν μία νέα μάσκα ,ή να καταναλώνουν την μάσκα που συσχετίζεται με την είσοδο. "
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "masking_and_padding.ipynb",
      "private_outputs": true,
      "provenance": [],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
